package DAO.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import model.impl.Schedule;
import model.impl.Station;
import model.impl.Train;

import org.hibernate.Query;

import DAO.HibernateUtil;
import DAO.ScheduleDAO;
import DAO.StationDAO;
import DAO.TrainDAO;
import DTO.DTOManager;
import DTO.ScheduleDTO;

/**
 *  Implements ScheduleDAO interface, extend GenericDAOImpl class using as 
 *  parameters entity of Schedule and Integer class as an ID. It also adds some 
 *  specific to Schedule entity methods
 */
public class ScheduleDAOImpl extends GenericDAOImpl<Schedule, Integer>
		implements ScheduleDAO {
	
	private StationDAO stationDAO = new StationDAOImpl();
	private TrainDAO trainDAO = new TrainDAOImpl();

	/**
	 * Looks for all schedules that satisfy the given in parameters requirements
	 * 
	 * @param beginStation - departure station 
	 * @param endStation   - arrival station
	 * @param beginTime	   - date of departure
	 * @param endTime	   - date of arrival
	 * @return			   - List of ScheduleDTO which satisfy given parameters
	 * 					     or null if such Schedules was not found
	 */
	public List<ScheduleDTO> findRequiredTrains(Station beginStation,
			Station endStation, Date beginTime, Date endTime) {
		List<ScheduleDTO> schedulesDTO = null;
		List<Schedule> schedules = null;
		Date currentDate = new Date();
		String sqlRequest = "SELECT s FROM Schedule s WHERE s.station1 = :departure "
				+ "AND s.station2 = :arrival AND s.dateOfDeparture BETWEEN :dateBegin " 
				+ "AND :dateEnd AND s.dateOfDeparture > :current";
		Query query = this.getSession().createQuery(sqlRequest)
				.setParameter("departure", beginStation)
				.setParameter("arrival", endStation)
				.setParameter("dateBegin", beginTime)
				.setParameter("dateEnd", endTime)
				.setParameter("current", currentDate);
		schedules = this.findMany(query);
		if (schedules != null && !schedules.isEmpty()) {
			schedulesDTO = new ArrayList<ScheduleDTO>();
			for (int i = 0; i < schedules.size(); i++) {
				schedulesDTO
						.add((new DTOManager()).createScheduleDTO(schedules.get(i)));
			}
		}
		return schedulesDTO;
	}

	/**
	 * Looks for all schedules that satisfy the given in parameters DTO object
	 * 
	 * @param scheduleDTO  - which DTO object should Schedule instance satisfy
	 * @return			   - Schedule entity object which satisfy given DTO
	 * 					     or null if such Schedule was not found
	 */
	public Schedule findScheduleByScheduleDTO(ScheduleDTO scheduleDTO) {
		Schedule schedule = null;
		TrainDAO trainDAO = new TrainDAOImpl();
		Train train = trainDAO.findTrainByTrainNumber(scheduleDTO.getTrainNumber());
		Station station1 = stationDAO.findStationByName(scheduleDTO.getStation1());
		Station station2 = stationDAO.findStationByName(scheduleDTO.getStation2());
		String sqlRequest = "SELECT s FROM Schedule s WHERE s.station1 = :departure "
				+ "AND s.station2 = :arrival AND s.dateOfDeparture = :dateBegin AND " +
				"s.dateOfArrival = :dateEnd AND s.train = :tr";
		Query query = this.getSession().createQuery(sqlRequest)
				.setParameter("departure", station1)
				.setParameter("arrival", station2)
				.setParameter("dateEnd", scheduleDTO.getDateOfArrival())
				.setParameter("tr", train)
				.setParameter("dateBegin", scheduleDTO.getDateOfDeparture());
		schedule = findOne(query);
		return schedule;
	}
	
	/**
	 * Checks all schedules and determine that a ScheduleDTO is not dissonantly to
	 * all Schedules
	 * 
	 * @param scheduleDTO - parameter for DTO object which it's required to check 
	 * 						for not to be dissonantly
	 * @return            - list of Schedule entity objects which contradict to the
	 * 						given DTO, or null if such Schedules were not found
	 */
	public List<Schedule> findIsSchedulePossible(ScheduleDTO scheduleDTO) {
		List<Schedule> schedules = null;
		Train train = trainDAO.findTrainByTrainNumber(scheduleDTO.getTrainNumber());
		String sqlRequest = "SELECT s FROM Schedule s WHERE  s.train = :tr  AND " +
				"(s.dateOfDeparture BETWEEN :dateBegin AND :dateEnd) AND" +
				"(s.dateOfArrival BETWEEN :dateBegin AND :dateEnd)";
		System.out.println(scheduleDTO.getDateOfDeparture().toString() + train.getPlaces());
		Query query = this.getSession().createQuery(sqlRequest)
				.setParameter("tr", train)
				.setParameter("dateBegin", scheduleDTO.getDateOfDeparture())
				.setParameter("dateEnd", scheduleDTO.getDateOfArrival());
		schedules = findMany(query);
		return schedules;
	}

	/**
	 * Checks given station and looks for all schedules that has this station as
	 * departure or arrival one.
	 * 
	 * @param  station - Station entity for which to look for Schedule
	 * @return         - List of Schedules which has the given station as an instance 
	 * 					 variable or null if nothing was found
	 */
	 public List<Schedule> findAllSchedulesByStation(Station station) {
		List<Schedule> schedules = null;

		String sqlRequest = "SELECT s FROM Schedule s WHERE s.station1 = :station "
				+ "OR s.station2 = :station";
		Query query = HibernateUtil.getSession().createQuery(sqlRequest)
				.setParameter("station", station);
		schedules = findMany(query);

		return schedules;
	}
}
